
#include "Precomp.h"
//
// The trace message header (.tmh) file must be included in a source file
// before any WPP macro calls and after defining a WPP_CONTROL_GUIDS
// macro (defined in toaster.h). During the compilation, WPP scans the source
// files for DoTraceMessage() calls and builds a .tmh file which stores a unique
// data GUID for each message, the text resource string for each message,
// and the data types of the variables passed in for each message.  This file
// is automatically generated by the WPP preprocessor.
//
#include "PCIO.tmh"

#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, EvtDeviceAdd)
#pragma alloc_text (PAGE, EvtDevicePrepareHardware)
#pragma alloc_text (PAGE, EvtDeviceReleaseHardware)
#pragma alloc_text (PAGE, EvtDeviceD0Exit)
#pragma alloc_text (PAGE, EvtDriverContextCleanup)
#endif

// These static variables having to do with the mapping of the HW io space into the user application process space 
// need to be accessible from the  FileCreat, FileClose and DeviceIOCtrl callbacks which don't all provide access to the
// the device context(FileClose and FileCleanup), or require a lengthy path to get to the device context (IOCTRL queue callback). 
// Since there is only one instance of the IO adapter at any time it is consider save to have a single
// global instance of them outside of the device context. 
PUSHORT	MemIoBase;		// The memory mapped hardware address space. Only driver code can access HW through this
static ULONG	MemIoLength;	// The length of the hardware mapped address space in bytes -- 4K 
static PMDL		my_mdl = NULL;  // memory descriptor for mapping the hardware address space into user mode memory in the scope of the calling application
static PVOID	UserSpaceMemIO_ptr = NULL;	// The user mode  pointer to mapped HW address space -- Only the calling application that open the device for IOCTRL can use this  
static UCHAR	N_adapter = 0;	// This counts the number of AOI_PCIO adapters present on the PCI bus and prevents all but the first instance from installing

// Free all the resources allocated in DriverEntry upon unloading of Driver.
VOID
EvtDriverContextCleanup(WDFOBJECT Driver)
{
	PAGED_CODE();
	WPP_CLEANUP(WdfDriverWdmGetDriverObject(Driver));		// this closes the WPP trace mechanism on the driver unload
}


VOID
EvtDriverUnload(WDFDRIVER Driver)
{
	//UNREFERENCED_PARAMETER(Driver);
	TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Driver unload (0x%p) ", Driver);
}



// This is the event call back that the frameworks calls when appliation code opens a file handle 
// to the device via an FileCreate() call. 
// This callbak executes in the thread context of the calling application and is used to create the 
// the user space memory mapped pointer to the HW registers of the adapter and attached controller.
// The function doesn't return thepointer directly, but stores it and eventually returns it via 
// a Device IO control call (getMemMap).
// See the Microsoft link for further explanation on how the mapping works and what caveats it presents.
// http://msdn.microsoft.com/en-us/library/windows/hardware/ff554629%28v=vs.85%29.aspx

VOID _Use_decl_annotations_
EvtDeviceFileCreate(IN WDFDEVICE Device, IN WDFREQUEST Request, IN WDFFILEOBJECT FileObject)
{
	UNREFERENCED_PARAMETER(Device); //	PDEVICE_EXTENSION   devExt = GetDeviceContext(Device);
	UNREFERENCED_PARAMETER(FileObject);

	volatile PVOID p;

	TraceEvents(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE, "Pcio DeviceCreateFile callback");

	// We allow only one connection the the hardware. This returns with a failure if the address space is already mapped because
	// someone already opened a file handle via CreateFile() before and has not closed the file yet.

	if (UserSpaceMemIO_ptr != NULL)
	{
		WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
		return;
	}

	my_mdl = NULL;
	my_mdl = IoAllocateMdl((PVOID)MemIoBase, MemIoLength, FALSE, FALSE, NULL);
	if (my_mdl)
	{
		MmBuildMdlForNonPagedPool(my_mdl);
	}
	else
	{	// fail the request and exit
		WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
		return;
	}
	
	// this tosses an exception if it fails
	try 
	{
		UserSpaceMemIO_ptr = MmMapLockedPagesSpecifyCache(my_mdl, UserMode, MmNonCached, NULL, FALSE, NormalPagePriority);  // needs to execute in user mode context.. File Create runs in User mode context
	} 
	except(EXCEPTION_EXECUTE_HANDLER) 
	{
		/* Error handling code */
		IoFreeMdl(my_mdl);
		my_mdl = NULL;
		WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
		return;
	}
	
	// in case there is some other error that yields a NULL from MmMapLockedPagesSpecifyCache above
	if ((p = UserSpaceMemIO_ptr) == NULL)
	{
		IoFreeMdl(my_mdl);
		my_mdl = NULL;
		WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
		return;
	}
	
	WdfRequestComplete(Request, STATUS_SUCCESS);  // if here all went well 
}

VOID // _Use_decl_annotations_
EvtDeviceFileClose(_In_ WDFFILEOBJECT FileObject)
{
	UNREFERENCED_PARAMETER(FileObject);

	TraceEvents(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE, "Pcio DeviceFileClose  callback");
}

// This Event callback function is called when an application closes its file handle to the device and no longer needs to access it.
// The user space memory mapping is destroyed and pointers to it are nulled out.
// Care must be taken that the user application doesn't keep using the now deleted mapping to the Hardware registers 
// after this call. A BSOD will otherwise occur.
// This code could probably also execute in the EvtDeviceFileClose() above, but some documentation pointed to the Cleanup 
// function as beeing a better choice. EvtDeviceFileCleanup() executes before EvtDeviceFileClose().

VOID // _Use_decl_annotations_
EvtDeviceFileCleanup(_In_ WDFFILEOBJECT FileObject)
{
	UNREFERENCED_PARAMETER(FileObject);

	TraceEvents(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE, "Pcio DeviceFileCleanup  callback");

	// unlock and delete the memory descriptor list for the user mapped address space.
	if (UserSpaceMemIO_ptr && my_mdl)
	{
		MmUnmapLockedPages(UserSpaceMemIO_ptr, my_mdl);
		UserSpaceMemIO_ptr = NULL;
	}

	if (my_mdl)
	{
		IoFreeMdl(my_mdl);
		my_mdl = NULL;
	}
}

// This function presents the IOControl handler of the driver. It is here where the IOcontrol calls from the application
// get handled and results are beeing returned, or in the case of GetNextInterrupt get requeued to a differen Request queue


VOID
EvtDeviceIoControl(IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t OutputBufferLength, IN size_t InputBufferLength, IN ULONG IoControlCode)
{
	UNREFERENCED_PARAMETER(Queue);		//	PDEVICE_EXTENSION   devExt = GetDeviceContext(WdfIoQueueGetDevice(Queue));
	UNREFERENCED_PARAMETER(OutputBufferLength);
	UNREFERENCED_PARAMETER(InputBufferLength);

	NTSTATUS status = STATUS_SUCCESS;
	size_t size;
	USHORT * InBuf = NULL;
	USHORT * OutBuf = NULL;
	PUSHORT *out = NULL;
	USHORT ndx;
	USHORT val;
	PDEVICE_EXTENSION   devExt = NULL;

	switch (IoControlCode)
	{
		case PCIO_IOCTL_GetVersion:
			TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "EvtDeviceIoControl: Revision request");
			const char* Revision = TERAPCIO_REVISION;		// This is a revision indicating the DeviceIOctrl interface 
			size = strlen(Revision) + 1;
			status = WdfRequestRetrieveOutputBuffer(Request, size, &OutBuf, NULL);

			if (status == STATUS_SUCCESS)
			{
				strcpy((PCHAR)OutBuf, Revision);
				WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, size);
				return;
			}

			break;

		case PCIO_IOCTL_GetMemMap:
			// This exports the user space mapped hardware memory map pointer that was created in the EvtDeviceFileCreate()
			// This provides the fastest possible way for the application to talk to the hardware registers, but also 
			// incurs the risk that an illegal memory access is generated if this pointer is used by the application with 
			// an invalid index that is not within the mapped space.
			// Also the user memory pointer is only valid as long the application has an open file handle to the device since
			// the mapping is deleted when the device is closed.
			TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "EvtDeviceIoControl: GETMemMap request");
			size = 2 * sizeof(PUSHORT);
			status = WdfRequestRetrieveOutputBuffer(Request, size, &OutBuf, NULL);

			if (status == STATUS_SUCCESS && UserSpaceMemIO_ptr != NULL)
			{
				out = (PUSHORT *)OutBuf;
				*out = UserSpaceMemIO_ptr;		// send the user space memory mapped address space to the caller. 
				out++;							// For compatibility reasons with previous driver we send a NULL 
				*out = NULL;					// for where the  timer event buffer would have been.

				WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, size);
				return;
			}
			else
				status = STATUS_INVALID_DISPOSITION;

			break;

		case PCIO_IOCTL_Read_IO:
			// This  is an alternate and safer way to read hardware registers since we can check the register index fist
			// however this method is slower since the entire read has to go forth and back through an IOCONTROL call 
			TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "EvtDeviceIoControl:Read IO");
			if (MemIoBase == NULL)
			{
				status = STATUS_INVALID_DISPOSITION;
				break;
			}

			size = sizeof(USHORT);

			if ((status = WdfRequestRetrieveInputBuffer(Request, size, &InBuf, NULL)) != STATUS_SUCCESS)
				break;

			if ((status = WdfRequestRetrieveOutputBuffer(Request, size, &OutBuf, NULL)) != STATUS_SUCCESS)
				break;

			ndx = InBuf[0];

			if (ndx >= MemIoLength)
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}

			*(OutBuf) = READ_REGISTER_USHORT(&MemIoBase[ndx]);
			WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, size);
			return;
			break;		// obviously redundant but left here for clarity

			// See comment for _Read_IO:
		case PCIO_IOCTL_Write_IO:
			TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "EvtDeviceIoControl:Write IO");
			if (MemIoBase == NULL)
			{
				status = STATUS_INVALID_DISPOSITION;
				break;
			}

			size = 2 * sizeof(USHORT);

			if ((status = WdfRequestRetrieveInputBuffer(Request, size, &InBuf, NULL)) != STATUS_SUCCESS)
				break;

			ndx = InBuf[0];
			val = InBuf[1];

			if (ndx >= MemIoLength)
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}

			WRITE_REGISTER_USHORT(&MemIoBase[ndx], val);
			WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, 0);
			return;
			break; // obviously redundant but left here for clarity

		case PCIO_IOCTL_GetNextInterrupt:
			devExt = GetDeviceContext(WdfIoQueueGetDevice(Queue));		// need to get the manual queue object from the device to forward request to
			status = WdfRequestForwardToIoQueue(Request, devExt->IntQueue);	// this can only fail because of coding errors 
			// Importnat NOTE:
			// This is the ONLY place where all interrupts can be (re-)enabled. 
			// For interrupts to be eflected to the application, the GetNextInterrupt request has to first be placed in it's
			// own queue before ALL_INT_EN can be set in order not to miss interrups and to cause a situation where interrupts
			// are permanently disabled because of a race condition.
			WRITE_REGISTER_USHORT(&MemIoBase[PCIO_INT_SEL], ALL_INT_EN);
			return;		// Nothing to complete -- Request has been sent to an other queue and is pending there 
						// until the EvtInterruptDpc dequeues it and completes it or it is beeing canceled.
			break;		// While in the IntQueue the request is cancel-able -- Provided by the framework

		default:
			// Unrecognised IOCTL request
			TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "Device IO: Invalid IOCTL call\n");
			status = STATUS_INVALID_DEVICE_REQUEST;
			break;
	}

	WdfRequestComplete(Request, status);

}

// This is the "main" of Device driver
// This is the first call into the driver code after the driver got loaded into memory.
// Its function is to present call backs to the framework to instanciate and remove a 
// device  (EvtDeviceAdd and EvtDriverUnload). The driver will only get created if the 
// call to WdfDriverCreate() succeeds -- otherwise the driver gets unloaded right away.
NTSTATUS
DriverEntry( IN PDRIVER_OBJECT  DriverObject,IN PUNICODE_STRING RegistryPath )
{
    NTSTATUS				status = STATUS_SUCCESS;
	WDF_DRIVER_CONFIG		config = { 0 };
	WDF_OBJECT_ATTRIBUTES	attributes = { 0 };
	WDFDRIVER				driver = NULL;

    WPP_INIT_TRACING( DriverObject, RegistryPath );			// Initialize WDF WPP tracing for dynamic trace messages 
    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT,"Pcio in DriverEntry.");		// TraceEvents is a macro created by the WPP feature enabled in the project settings
	
	N_adapter = 0;
    WDF_DRIVER_CONFIG_INIT( &config, EvtDeviceAdd );		// init the config structure to convey the DeviceAdd function callback for PNP AddDevice

	config.EvtDriverUnload = EvtDriverUnload;
	config.EvtDriverDeviceAdd = EvtDeviceAdd;				// same as above 

    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);					// init to all 0's
    attributes.EvtCleanupCallback = EvtDriverContextCleanup;	// Register a clean up callback so that we can call WPP_CLEANUP when driver unloads

	attributes.SynchronizationScope = WdfSynchronizationScopeDevice;

    
	status = WdfDriverCreate( DriverObject, RegistryPath, &attributes, &config, &driver);					

    if (!NT_SUCCESS(status)) 
	{
        TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "In DriverEntry: WdfDriverCreate failed with status %!STATUS!", status);
        WPP_CLEANUP(DriverObject);	// Must call WPP_CLEANUP directly since Driver was not created and DriverContextCleanup therefore will not get called.
	}
	else
	{
		TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Driver created : (0x%p) ", driver);
	}
    return status;	// if not NT_SUCCESS driver will not load
} // Driver_Entry


/*++

Routine Description: EvtDeviceAdd( WDFDRIVER  Driver, PWDFDEVICE_INIT  DeviceInit)

DeviceAdd is called by the framework in response to AddDevice
call from the PnP manager. Here the driver should register all the
PNP, power and Io callbacks, register interfaces and allocate other
software resources required by the device. The driver can query
any interfaces or get the config space information from the bus driver
but cannot access hardware registers or initialize the device.

Arguments:

Return Value: Must return with STATUS_SUCCESS or else the the framework deletes the device object and all its children
--*/
NTSTATUS
EvtDeviceAdd(WDFDRIVER  Driver, PWDFDEVICE_INIT  DeviceInit)
{
	NTSTATUS						status = STATUS_SUCCESS;
	WDF_PNPPOWER_EVENT_CALLBACKS	pnpPowerCallbacks;		// structure to convey the PNP callback functions to WDF
	WDF_OBJECT_ATTRIBUTES			attributes;				// structure to 
	WDFDEVICE						device;
	PDEVICE_EXTENSION				devExt = NULL;
	WDF_IO_QUEUE_CONFIG				ioQConfig;				// Struct for IO queue config 
	WDF_FILEOBJECT_CONFIG			fileCreateCloseConfig;

	UNREFERENCED_PARAMETER(Driver);

	TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> In DeviceAdd");

	PAGED_CODE();

	// The machine permits only one adapter, so we immediately fail a request to add a second AOI_PCIO adapter should one be present in the system.
	if (N_adapter)
	{
		TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "DeviceCreatetion failed: Only a single AOI_PCIO adapter can be present in a system");
		return  STATUS_UNSUCCESSFUL;
	}

	// Configure the callbacks for FileCreate and FileClose from the user mode application 
	WDF_FILEOBJECT_CONFIG_INIT(&fileCreateCloseConfig, EvtDeviceFileCreate, EvtDeviceFileClose, EvtDeviceFileCleanup); 
	WdfDeviceInitSetFileObjectConfig(DeviceInit, &fileCreateCloseConfig, WDF_NO_OBJECT_ATTRIBUTES);		// Register the callback functions


	WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);		 // This applies to any IO other the IOCTRLs, which are individually defined by the IOCTRL token 

	WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);						// init to to all 0's
	pnpPowerCallbacks.EvtDevicePrepareHardware = EvtDevicePrepareHardware;		// function to get HW ready for use
	pnpPowerCallbacks.EvtDeviceReleaseHardware = EvtDeviceReleaseHardware;		// function to disable HW before unloading, exiting Driver/Device
	// These two callbacks set up and tear down hardware state that must be done every time the device moves in and out of the D0-working state.
	pnpPowerCallbacks.EvtDeviceD0Entry = EvtDeviceD0Entry;						// 
	pnpPowerCallbacks.EvtDeviceD0Exit  = EvtDeviceD0Exit;						//
	
	WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);		// register the callback functions

	WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_EXTENSION);		// reserve and initialize storage for the device context aka DeviceExtension


	// By opting for SynchronizationScopeDevice, we tell the framework to
	// synchronize callbacks events of all the objects directly associated
	// with the device. In this driver, we will associate queues and
	// and DpcForIsr. By doing that we don't have to worry about synchronizing
	// access to device-context by Io Events and DpcForIsr because they would
	// not execute concurrently ever. Framework will serialize them by using an
	// internal device-lock.
	//
	attributes.SynchronizationScope = WdfSynchronizationScopeDevice;
	//attributes.SynchronizationScope = WdfSynchronizationScopeNone;
	
	if (!NT_SUCCESS(status = WdfDeviceCreate(&DeviceInit, &attributes, &device)))	// Create the device according to attributes
	{
		TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "DeviceCreatetion  failed %!STATUS!", status);
		return status;
	}

	//
	// Get the DeviceExtension.
	// GetDeviceContext is an in-line function
	// defined by WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro in the
	// private header file. This function will do the type checking and return
	// the device context. If you pass a wrong object a wrong object handle
	// it will return NULL and assert if run under framework verifier mode.
	//
	devExt = GetDeviceContext(device);
	devExt->Device = device;			// Store the handle to the device for later when we need it to register the interrupt object

	TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, " AddDevice PDO (0x%p) FDO (0x%p), DevExt (0x%p)", WdfDeviceWdmGetPhysicalDevice(device), WdfDeviceWdmGetDeviceObject(device), devExt);


	// Create the Device IO control queue  
	WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQConfig,	WdfIoQueueDispatchSequential);
	// WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQConfig, WdfIoQueueDispatchParallel);
	ioQConfig.EvtIoDeviceControl = EvtDeviceIoControl;		// the handler for the default IO queue 

	status = WdfIoQueueCreate(device, &ioQConfig,WDF_NO_OBJECT_ATTRIBUTES,	&devExt->IOCTLQueue);
	
	if (!NT_SUCCESS(status))
	{
		TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfIoQueueCreate for Default queue failed with status 0x%08x\n", status);
		return status;
	}

	// create a second, manual dispatch queue for interrupt reflection back to the application out of isrDPC
	WDF_IO_QUEUE_CONFIG_INIT(&ioQConfig,WdfIoQueueDispatchManual);

	status = WdfIoQueueCreate(device, &ioQConfig,WDF_NO_OBJECT_ATTRIBUTES,	&devExt->IntQueue);
	
	if (!NT_SUCCESS(status))
	{
		TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfIoQueueCreate for interrupt queue failed with status 0x%08x\n", status);
		return status;
	}

#ifdef CreateWithGUIDInterface
	// finally,  create a Device Interface so that the application can connect to the driver via the shared knowledge of the  GUID 
	// This is the more up to date method of providing an interface to an application, but it requires multiple calls to the registry 
	// from the application to arrive at a symbolic pathname by which the device can be opened. 
	status = WdfDeviceCreateDeviceInterface(device, (LPGUID)&GUID_DEVINTERFACE_AOI_PCIO, NULL);

	if (!NT_SUCCESS(status)) 
	{
		TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceCreateDeviceInterface failed %!STATUS!", status);
		return status;
	}
#else

	// This creates a shared Symbolic link for the application to open via win32 function CreatFile().
	// This method of interfacing to an application is easier that using the registry heavy option of CreateDeviceInterface 
	// This is the method and symbolic name the old NTDriver Terapcio.sys used to provide its interface by.
	// The application opens the device using a symbolic file name like this "\\\\.\\AOIPcioDevice0" in CreateFile for the file name.
	// see matching defines in aoi_pcioIOctrl.h

	DECLARE_CONST_UNICODE_STRING(sLinkName, DRIVER_SYMBOLIC_NAME_driver);
	
	status = WdfDeviceCreateSymbolicLink( device, &sLinkName );
	if (!NT_SUCCESS(status)) 
	{
		TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceCreateSymbolicLink failed %!STATUS!", status);
		return status;
	}
#endif


	InterruptCreate(devExt);

	TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, " DeviceAdd %!STATUS!", status);
	N_adapter++;	// increment the adapter count -- We can only have a single adapter per machine.
	return status;
}



/*++

Routine Description: EvtDevicePrepareHardware(WDFDEVICE Device,WDFCMRESLIST Resources,WDFCMRESLIST ResourcesTranslated)

    Performs whatever initialization is needed to setup the device
		Getting the memory mapped hardware range from the PCI BAR and mapping it into address space that 
		can be accessed within the driver
  

Arguments:

    Device - A handle to the WDFDEVICE

    Resources - The raw PnP resources associated with the device. -- Not needed

    ResourcesTranslated - The translated PnP resources associated with the
        device.  This provides us with the cpu hardware address of the memory mapped range and its size. 

Return Value:

    NT status code - failure will result in the device stack being torn down

--*/
NTSTATUS 
EvtDevicePrepareHardware(WDFDEVICE Device,WDFCMRESLIST Resources,WDFCMRESLIST ResourcesTranslated)
{	
	PAGED_CODE();
	UNREFERENCED_PARAMETER(Resources);
	UNREFERENCED_PARAMETER(Device); //	PDEVICE_EXTENSION   devExt = GetDeviceContext(Device);

	PCM_PARTIAL_RESOURCE_DESCRIPTOR desc = NULL;

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "DevicePrepareHardware: getting the resources for the mem-map ");

	//
	// Parse the resource list and save the pertinent resource information.
	//
	MemIoBase = NULL;
		
	for (unsigned int i = 0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++) 
	{
		// get the resource descriptors 
		desc = WdfCmResourceListGetDescriptor(ResourcesTranslated, i);	

		if (!desc)
		{
			TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,"WdfResourceCmGetDescriptor failed");
			continue; 
		}
	
		// All we care about is THE memory mapped space -- The port range is not needed and the interrupt is handled directly by the WDF 
		if (desc->Type == CmResourceTypeMemory && desc->u.Memory.Length == PLX9050_MemMapSize)
		{
			// map the physical address to a memory mapped address space the driver can read and write from 
			MemIoBase = (PUSHORT) MmMapIoSpace( desc->u.Memory.Start, desc->u.Memory.Length, MmNonCached);


			if (MemIoBase != NULL)
			{
				MemIoLength = desc->u.Memory.Length;	// we need this again to un-map the range
				TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "HW Memory mapped at  (0x%p),  with %d bytes", MemIoBase, MemIoLength);
				return STATUS_SUCCESS;
			}
			else
			{
				// if we didn't get a resource or the resource did not map to an address space we are done -- we can't talk to the HW ever
				TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Did not get the hw-IO space mapped into memory --  can't talk to HW -- failed");
			}
		}

	}
	

	//return STATUS_SUCCESS;
	 return STATUS_INSUFFICIENT_RESOURCES;
    
}


/*++

Routine Description: EvtDeviceReleaseHardware(IN  WDFDEVICE Device,IN  WDFCMRESLIST ResourcesTranslated )

    Unmap the resources that were mapped in DevicePrepareHardware.
    This will only be called when the device stopped for resource rebalance,
    surprise-removed or query-removed.

Arguments:

    Device - A handle to the WDFDEVICE

    ResourcesTranslated - The translated PnP resources associated with the
        device.  

Return Value:

    NT status code - failure will result in the device stack being torn down

--*/
NTSTATUS
EvtDeviceReleaseHardware(IN  WDFDEVICE Device, IN  WDFCMRESLIST ResourcesTranslated )
{	
	PAGED_CODE(); 
	UNREFERENCED_PARAMETER(ResourcesTranslated);
	UNREFERENCED_PARAMETER(Device); //	PDEVICE_EXTENSION   devExt = GetDeviceContext(Device);

	
    if (MemIoBase) 
	{
		WRITE_REGISTER_USHORT(&MemIoBase[PCIO_INT_SEL], ALL_INT_DIS);		// make sure the HW is not firing interrupts any-more 
		MmUnmapIoSpace(MemIoBase, MemIoLength);
        MemIoBase = NULL;
		MemIoLength = 0;
    }

	TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "Device released hardware IO memory mappings");

	return STATUS_SUCCESS;
}



/*++

Routine Description: EvtDeviceD0Entry( IN  WDFDEVICE Device,IN  WDF_POWER_DEVICE_STATE PreviousState)

    This routine prepares the device for use.  It is called whenever the device
    enters the D0 state, which happens when the device is started, when it is
    restarted, and when it has been powered off.

    Note that interrupts will not be enabled at the time that this is called.
    They will be enabled after this callback completes.

    This function is not marked page-able because this function is in the
    device power up path. When a function is marked page-able and the code
    section is paged out, it will generate a page fault which could impact
    the fast resume behaviour because the client driver will have to wait
    until the system drivers can service this page fault.

Arguments:

    Device  - The handle to the WDF device object

    PreviousState - The state the device was in before this callback was invoked.

Return Value:

    NTSTATUS

    Success implies that the device can be used.

    Failure will result in the    device stack being torn down.

--*/

#define TMR_TESTVAL1 0x1234
#define TMR_TESTVAL2 0xcdef 
NTSTATUS
EvtDeviceD0Entry( IN  WDFDEVICE Device,IN  WDF_POWER_DEVICE_STATE PreviousState)
{
	UNREFERENCED_PARAMETER(PreviousState);
	UNREFERENCED_PARAMETER(Device); //	PDEVICE_EXTENSION   devExt = GetDeviceContext(Device);
	// This is not page-able -- see above

	NTSTATUS            status = STATUS_SUCCESS;
	volatile USHORT		tmp; 

	TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "DeviceD0Entry called,connect to, and reset hardware");

	
	// initialize the HW by issuing a reset -- Leave interrupts disabled
	WRITE_REGISTER_USHORT(&MemIoBase[PCIO_RESET], RST_ALL);				// this generates a reset pulse
	WRITE_REGISTER_USHORT(&MemIoBase[TMR1_CTRL], TMR_CTRL_RESET);			// these lines are here to make the pulse width longer
	WRITE_REGISTER_USHORT(&MemIoBase[TMR2_CTRL], TMR_CTRL_RESET);			// so that the pulse can travel down the cable to the 
	WRITE_REGISTER_USHORT(&MemIoBase[TMR3_CTRL], TMR_CTRL_RESET);			// attached controller 
	tmp = READ_REGISTER_USHORT(&MemIoBase[PCIO_INT_READ]);					// read and clear any potential interrupt flags 
	WRITE_REGISTER_USHORT(&MemIoBase[PCIO_RESET], RST_NONE);				// release from reset 
	WRITE_REGISTER_USHORT(&MemIoBase[PCIO_INT_SEL], ALL_INT_DIS);			// this call is not necessary as it's the reset default, but placed here for clarity
	

	// execute some simple read back tests to make sure we are talking to the HW
	// if something goes wrong here the hardware is bad -- in that case we exit with AdapterHardware error
	// which unloads the device driver and  puts a yellow exclamation mark on the device in DeviceManager
	
	WRITE_REGISTER_USHORT(&MemIoBase[PCIO_STRB_CTRL], A_STRB);			// Setting the "A_Strobe set -- Bit should be reflected in Reg PCIO_CONFIG
	tmp = READ_REGISTER_USHORT(&MemIoBase[PCIO_CONFIG]);				// this also returns the HW version "0xb1" in the upper byte

	if (!tmp & STROBE_A_RFLECT_BIT)
	{
		TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "PCIO failure: Analog Strobe bit is not reflected in PCIO_CONFIG register");
		status = STATUS_ADAPTER_HARDWARE_ERROR;
	}

	WRITE_REGISTER_USHORT(&MemIoBase[PCIO_STRB_CTRL], D_STRB);// Reset to D-Strobes, the AOI controller exclusively listens on D-Strobes 
	tmp = READ_REGISTER_USHORT(&MemIoBase[PCIO_CONFIG]);
	if (tmp & STROBE_A_RFLECT_BIT)
	{
		TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "PCIO failure: Analog Strobe bit is not reflected in PCIO_CONFIG register");
		status = STATUS_ADAPTER_HARDWARE_ERROR;
	}

	// Testing a timer register to see if we can write and read the 16 bit values correctly
	WRITE_REGISTER_USHORT(&MemIoBase[TMR_BASE_LSW + TMR1], TMR_TESTVAL1);
	WRITE_REGISTER_USHORT(&MemIoBase[TMR_BASE_MSW + TMR1], TMR_TESTVAL2);
	tmp = READ_REGISTER_USHORT(&MemIoBase[TMR_BASE_LSW + TMR1]);

	
	if (tmp != TMR_TESTVAL1)
	{
		TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "PCIO TMR_LSB test failure read: 0x%x, expected 0x%x", tmp, TMR_TESTVAL1);
		status = STATUS_ADAPTER_HARDWARE_ERROR;
	}

	tmp = READ_REGISTER_USHORT(&MemIoBase[TMR_BASE_MSW + TMR1]);

	
	if (tmp != TMR_TESTVAL2)
	{
		TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "PCIO TMR_LSB test failure read: 0x%x, expected 0x%x", tmp, TMR_TESTVAL2);
		status = STATUS_ADAPTER_HARDWARE_ERROR;
	}

	WRITE_REGISTER_USHORT(&MemIoBase[TMR1_CTRL], TMR_CTRL_RESET); // clear timer reg again

	if (status == STATUS_SUCCESS)
		TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "PCIO communication established and io function verified");

    return status;
}


/*++

Routine Description: EvtDeviceD0Exit( IN  WDFDEVICE Device,IN  WDF_POWER_DEVICE_STATE TargetState)

    This routine undoes anything done in tDeviceD0Entry.  It is called
    whenever the device leaves the D0 state, which happens when the device
    is stopped, when it is removed, and when it is powered off.

    The device is still in D0 (operational)  when this callback is invoked, which means that
    the driver can still touch hardware in this routine.

    Note that interrupts have already been disabled by the time that this
    callback is invoked.

Arguments:

    Device  - The handle to the WDF device object

    TargetState - The state the device will go to when this callback completes.

Return Value:

    Success implies that the device can be used.  Failure will result in the
    device stack being torn down.

--*/
NTSTATUS
EvtDeviceD0Exit( IN  WDFDEVICE Device,IN  WDF_POWER_DEVICE_STATE TargetState)
{
	PAGED_CODE();
	UNREFERENCED_PARAMETER(Device); //	PDEVICE_EXTENSION   devExt = GetDeviceContext(Device);
	

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "Device leaving D0 state");

    switch (TargetState)
	{
		case WdfPowerDeviceD1:
			TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "Device D1 ");
			break;
		case WdfPowerDeviceD2:
			TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "Device D2 ");
			break;
		case WdfPowerDeviceD3:
			TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "Device D3 ");
			break;
		case WdfPowerDevicePrepareForHibernation:
			TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "Device Hibernate..");
			break;
		case WdfPowerDeviceD3Final:
		default:

			// We are going down !
			TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "DeviceD3 Final -- device is going to be shut down");
			if (MemIoBase)
				WRITE_REGISTER_USHORT(&MemIoBase[PCIO_INT_SEL], ALL_INT_DIS);		// Reset HW: make sure the HW is not firing interrupts any-more 

			break;
    }

	

    return STATUS_SUCCESS;
}





